﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using AgilePlannerDT.Data.Impl;
using AgilePlannerDT.Utils;
using AgilePlannerDT.Data;
using System.Windows.Media;
using System.Windows.Threading;


namespace AgilePlannerDT.Persister.Impl.Socket
{
    public class AsyncXMLSocketPersister : AbstractAsyncPersister, MessageSink
    {
        #region Attributes

        static SocketConnectionEndpoint client = null;
        Project project;
        Boolean GetProjectNamesRequested = false;
        Dispatcher dispatcher;
        bool isActiveCreateIteration = false;
        long client_id;
        private StoryCardQueue storyCardQueue = new StoryCardQueue();

        #endregion

        #region Constructors

        public AsyncXMLSocketPersister(IConnection connection, Dispatcher dispatcher)
        {

            if (client != null)
            {
                ((XMLSocketClient)client).ShutDown();
            }
            client = new XMLSocketClient(connection, this);
            project = new ProjectDataObject();
            this.dispatcher = dispatcher;

            //var theadedUpdate = new ThreadedUpdate(storyCardQueue, client);
            //var updateThread = new Thread();
        }

        #endregion

        #region Properties

        public override Project Project
        {
            get { return project; }
            set
            {
                this.project = new ProjectDataObject(value);
            }
        }

        #endregion

        #region Connection

        public override bool IsConnected()
        {
            throw new NotImplementedException();
        }

        public override void Connect(string projectName)
        {
            client.Send(new MessageDataObject(MessageDataObject.CONNECT, projectName));
        }

        public override void Disconnect()
        {
            ((XMLSocketClient)client).ShutDown();
            
        }

        public override void Login(string userName, string password)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region Load

        public override void Load(string projectName)
        {
            client.Send(new MessageDataObject(MessageDataObject.LOAD, projectName));
        }

        public override void Load(string projectName, DateTime start, DateTime end)
        {
            throw new NotImplementedException();
        }

        public override void GetProjectNames()
        {

            client.Send(new MessageDataObject(MessageDataObject.GET_PROJECT_NAMES));
            this.GetProjectNamesRequested = true;
        }

        #endregion

        #region Create

        public override void CreateBacklog(int width, int height, int locationX, int locationY)
        {
            client.Send(new MessageDataObject(MessageDataObject.CREATE_BACKLOG, new BacklogDataObject(width, height, locationX, locationY)));
        }

        public override void CreateIteration(string name, string description,
            int width, int height, int locationX, int locationY, float availableEffort,
            DateTime startDate, DateTime endDate)
        {
            this.isActiveCreateIteration = true;
            client.Send(new MessageDataObject(MessageDataObject.CREATE_ITERATION,
                new IterationDataObject(name, description, width, height, locationX, locationY,
                    availableEffort, startDate, endDate)));

        }

        public override void CreateProject(string projectName)
        {
            throw new NotImplementedException();
        }

        public override void CreateStoryCard(string name, string description, int width, int height,
            int locationX, int locationY, long parentid, float bestCaseEstimate,
            float mostlikelyEstimate, float worstCaseEstimate, float actualEffort,
            string status, string color, string cardOwner, float rotationAngle)
        {
            client.Send(new MessageDataObject(MessageDataObject.CREATE_STORYCARD,
                new StoryCardDataObject(parentid, name, description, width, height, locationX, locationY,
                    bestCaseEstimate, mostlikelyEstimate, worstCaseEstimate, actualEffort,
                    status, (Color)new CardColorConverter().ConvertFrom(color), cardOwner, rotationAngle)));
        }

        #endregion

        #region Delete

        public override void DeleteBacklog(long id)
        {
            throw new NotImplementedException();
        }

        public override void DeleteIteration(long id)
        {
            client.Send(new MessageDataObject(MessageDataObject.DELETE_CARD, id));
        }

        public override void DeleteProject(string projectName)
        {
            throw new NotImplementedException();
        }

        public override void DeleteStoryCard(long id)
        {
            client.Send(new MessageDataObject(MessageDataObject.DELETE_CARD, id));
            //  throw new NotImplementedException();
        }

        public override void AddTeamMember(TeamMember member)
        {
            client.Send(new MessageDataObject(MessageDataObject.ADD_USER, member));
        }

        public override void RemoveTeamMember(TeamMember member)
        {
            client.Send(new MessageDataObject(MessageDataObject.REMOVE_USER, member));
        }

        #endregion

        #region Undelete

        public override void UndeleteIteration(AgilePlannerDT.Data.Iteration iteration)
        {

            /*          foreach (StoryCard sc in iteration.StoryCardChildren)
                      {
                          //set the storycard parent as backlog and the iteration will collect it again
                          sc.Parent = Project.Backlog.ID;
                          UndeleteStoryCard(sc);
               
                      }

                      CreateIteration(iteration.Name, iteration.Description, (int)iteration.Width,
                      (int)iteration.Height, (int)iteration.LocationX, (int)iteration.LocationY,
                      iteration.AvailableEffort, iteration.StartDate, iteration.EndDate);
            
                    */

            client.Send(new MessageDataObject(MessageDataObject.UNDELETE_CARD, iteration));
            //    throw new NotImplementedException();
        }

        public override void UndeleteStoryCard(AgilePlannerDT.Data.StoryCard storycard)
        {
            //  CardConstants.CardColors.yellow.


            foreach (string color in CardConstants.ColorList.Keys)
            {
                if (CardConstants.ColorList[color].Equals(storycard.Background))
                {
                    /*           CreateStoryCard(storycard.Name, storycard.Description, (int)storycard.Width,
                               (int)storycard.Height, (int)storycard.LocationX, (int)storycard.LocationY,
                               storycard.Parent, storycard.BestCaseEstimate, storycard.MostlikelyEstimate,
                               storycard.WorstCaseEstimate, storycard.ActualEffort, storycard.Status,
                              color, storycard.CardOwner,
                             (float)storycard.RotationAngle);*/
                    client.Send(new MessageDataObject(MessageDataObject.UNDELETE_CARD, storycard));
                    return;
                }
            }

            //throw new NotImplementedException();
        }

        #endregion

        #region Move

        public override void MoveStoryCardToNewParent(AgilePlannerDT.Data.StoryCard sc, long newparentid, int locationX, int locationY, float rotationAngle)
        {

            client.Send(new MessageDataObject(0, MessageDataObject.MOVE_STORYCARD_TO_NEW_PARENT, 0, sc, new Dictionary<string, string>(){
                                                                                                                {"rotation",rotationAngle.ToString()},
                                                                                                                {"locationX",locationX.ToString()},
                                                                                                                {"locationY",locationY.ToString()},
                                                                                                                {"newparentid",newparentid.ToString()}}));
        }

        #endregion

        #region MouseMove

        public override void MouseMove(MouseMove move)
        {
            client.Send(new MessageDataObject(MessageDataObject.MOUSE_MOVE, move));
        }
        #endregion

        #region Update

        public override void UpdateBacklog(AgilePlannerDT.Data.Backlog backlog)
        {
            throw new NotImplementedException();
        }

        public override void UpdateIteration(AgilePlannerDT.Data.Iteration iteration)
        {
            client.Send(new MessageDataObject(MessageDataObject.UPDATE_CARD, iteration));
        }

        public override void UpdateLegend(AgilePlannerDT.Data.Legend legend)
        {
            client.Send(new MessageDataObject(MessageDataObject.UPDATE_LEGEND, legend));
        }

        public override void UpdateStoryCard(AgilePlannerDT.Data.StoryCard storycard)
        {
            //storyCardQueue.Offer(storycard);
            client.Send(new MessageDataObject(MessageDataObject.UPDATE_CARD,storycard));
        }

        public override void UpdateProject(AgilePlannerDT.Data.Project project)
        {
            client.Send(new MessageDataObject(MessageDataObject.UPDATE_PROJECT, project));
        }

        public override void LiveUpdate(LiveUpdate update)
        {
            client.Send(new MessageDataObject(MessageDataObject.KEYSTROKE, update));
        }

        public override void MinimizeCard(long ID)
        {
            client.Send(new MessageDataObject(MessageDataObject.MINIMIZE_CARD, ID));
        }

        public override void SynchWithJazz(int MessageType, Dictionary<string, string> data)
        {
            client.Send(new MessageDataObject(MessageType, data));
        }
        #endregion

        #region Project

        public override void ArrangeProject(AgilePlannerDT.Data.Project project)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region MessageSink Members

        public void Receive(AgilePlannerDT.Data.Message msg)
        {
            //  throw new NotImplementedException();
            //do all the checking for the different message types
            //if this. dispatch is null, that means the call is from the DistribtedPersiterTest.cs
            //if not, the call is from AgileMainWindow.cs
            switch (msg.MessageType)
            {
                case MessageDataObject.GET_PROJECT_NAMES:
                    if (GetProjectNamesRequested)
                    {
                        if (this.dispatcher == null)
                        {
                            GotProjectNames((List<String>)msg.Message);
                        }
                        else if (!this.dispatcher.CheckAccess())
                            this.dispatcher.BeginInvoke(DispatcherPriority.Render, new GotProjectNamesEventHandler(GotProjectNames), (List<String>)msg.Message);
                        this.GetProjectNamesRequested = false;
                    }
                    break;
                case MessageDataObject.CONNECT:
                    {
                        if (this.dispatcher == null)
                            LoadedProject((Project)msg.Message);
                        else if (!this.dispatcher.CheckAccess())
                            this.dispatcher.BeginInvoke(DispatcherPriority.Render, new LoadedProjectEventHandler(LoadedProject), (Project)msg.Message);
                    }
                    break;
                case MessageDataObject.CREATE_STORYCARD:
                    {
                        if (this.dispatcher == null)
                        {
                            CreatedStoryCard((StoryCard)msg.Message);
                        }
                        else if (!this.dispatcher.CheckAccess())
                            this.dispatcher.BeginInvoke(DispatcherPriority.Render, new CreatedStoryCardEventHandler(CreatedStoryCard), (StoryCard)msg.Message);
                    }
                    break;
                case MessageDataObject.CREATE_ITERATION:
                    {
                        if (this.dispatcher == null)
                        {
                            CreatedIteration((Iteration)msg.Message);
                        }
                        else if (!this.dispatcher.CheckAccess())
                        {
                            this.dispatcher.BeginInvoke(DispatcherPriority.Render, new CreatedIterationEventHandler(CreatedIteration), (Iteration)msg.Message, !this.isActiveCreateIteration);
                            this.isActiveCreateIteration = false;
                        }
                    }
                    break;
                case MessageDataObject.DELETE_CARD:
                    {
                        if (this.dispatcher == null)
                        {
                            if (msg.Message is StoryCard)
                                DeletedStoryCard((StoryCard)msg.Message);
                            else
                                DeletedIteration((Iteration)msg.Message);
                        }
                        else if (!this.dispatcher.CheckAccess())
                        {
                            if (msg.Message is StoryCard)
                                this.dispatcher.BeginInvoke(DispatcherPriority.Render, new DeletedStoryCardEventHandler(DeletedStoryCard), (StoryCard)msg.Message);
                            else
                                this.dispatcher.BeginInvoke(DispatcherPriority.Render, new DeletedIterationEventHandler(DeletedIteration), (Iteration)msg.Message);
                        }
                    }
                    break;
                case MessageDataObject.UPDATE_CARD:
                    if (this.dispatcher == null)
                    {
                        if (msg.Message is StoryCard)
                            UpdatedStoryCard((StoryCard)msg.Message);
                        else if (msg.Message is Iteration)
                            UpdatedIteration((Iteration)msg.Message);
                    }
                    else if (!this.dispatcher.CheckAccess())
                    {
                        if (msg.Message is StoryCard)
                            this.dispatcher.BeginInvoke(DispatcherPriority.Render, new UpdatedStoryCardEventHandler(UpdatedStoryCard), (StoryCard)msg.Message);
                        else if (msg.Message is Iteration)
                            this.dispatcher.BeginInvoke(DispatcherPriority.Render, new UpdatedIterationEventHandler(UpdatedIteration), (Iteration)msg.Message);
                    }
                    break;

                case MessageDataObject.MOUSE_MOVE:
                    if (!this.dispatcher.CheckAccess())
                    {
                        this.dispatcher.BeginInvoke(DispatcherPriority.Render, new MouseMoveEventHandler(MouseMoved), (MouseMove)msg.Message);
                    }
                    break;
                case MessageDataObject.DISCONNECT:
                    if (this.dispatcher == null)
                    {
                        OtherClientDisconnected((int)msg.Message);
                    }
                    else if (!this.dispatcher.CheckAccess())
                    {
                        this.dispatcher.BeginInvoke(DispatcherPriority.Render, new DisconnectEventHandler(OtherClientDisconnected), (int)msg.Message);
                    }
                    break;
                case MessageDataObject.MOVE_STORYCARD_TO_NEW_PARENT:
                    if (!this.dispatcher.CheckAccess())
                    {
                        //changing the card parent

                        this.dispatcher.BeginInvoke(DispatcherPriority.Render, new MovedStoryCardToNewParentEventHandler(MovedStoryCardToNewParent), (StoryCard)msg.Message);
                    }
                    break;
                case MessageDataObject.KEYSTROKE:
                    if (!this.dispatcher.CheckAccess())
                    {

                        this.dispatcher.BeginInvoke(DispatcherPriority.Render, new LiveUpdateEventHandler(LiveUpdated), (LiveUpdate)msg.Message);
                    }
                    break;

                //case MessageDataObject.EXCEPTION:
                //    if (!this.dispatcher.CheckAccess())
                //    {

                //        this.dispatcher.BeginInvoke(DispatcherPriority.Render, new ExceptionEventHandler(ExceptionCaptured), (long)msg.Message);
                //    }
                //    break;
                case MessageDataObject.UNDELETE_CARD:
                    if (!this.dispatcher.CheckAccess())
                    {
                        this.dispatcher.BeginInvoke(DispatcherPriority.Render, new UndeletedCardEventHandler(UndeletedCard), (IndexCard)msg.Message);
                    }
                    break;

                case MessageDataObject.START_VOTING:
                    if (!this.dispatcher.CheckAccess())
                    {
                        this.dispatcher.BeginInvoke(DispatcherPriority.Render, new StartVotingEventHandler(StartedVoting));
                    }
                    break;

                case MessageDataObject.STOP_VOTING:
                    if (!this.dispatcher.CheckAccess())
                    {
                        this.dispatcher.BeginInvoke(DispatcherPriority.Render, new StopVotingEventHandler(StoppedVoting));
                    }
                    break;
                case MessageDataObject.VOTE_CARD:
                    if (!this.dispatcher.CheckAccess())
                    {
                        this.dispatcher.BeginInvoke(DispatcherPriority.Render, new VoteCardEventHandler(VotedCard), (VoteDot)msg.Message);
                    }
                    break;
                case MessageDataObject.CLEAR_VOTES:
                    if (!this.dispatcher.CheckAccess())
                    {
                        this.dispatcher.BeginInvoke(DispatcherPriority.Render, new ClearVotesEventHandler(ClearedVotes));
                    }
                    break;

            }
        }

        #endregion

        #region Dot Voting

        public override void StartVoting()
        {
            client.Send(new MessageDataObject(MessageDataObject.START_VOTING));
        }

        public override void StopVoting()
        {
            client.Send(new MessageDataObject(MessageDataObject.STOP_VOTING));
        }

        public override void VoteCard(VoteDot dot)
        {
            client.Send(new MessageDataObject(MessageDataObject.VOTE_CARD, dot));
        }

        public override void ClearVotes()
        {
            client.Send(new MessageDataObject(MessageDataObject.CLEAR_VOTES));
        }

        #endregion

        public void stop()
        {
            if (client != null)
            {
                ((XMLSocketClient)client).ShutDown();
                client = null;
            }
        }


    }

    public class ThreadedUpdate
    {
        StoryCardQueue stqueue;
        SocketConnectionEndpoint client;
        public ThreadedUpdate(StoryCardQueue que, SocketConnectionEndpoint endclient)
        {
            stqueue = que;
            client = endclient;
        }

        public void UpdateStoryCard()
        {
            while (true)
            {
                client.Send(new MessageDataObject(MessageDataObject.UPDATE_CARD, stqueue.Take()));
            }
        }
    }

    public class StoryCardQueue
    {
        List<StoryCard> cards = new List<StoryCard>();

        public void Offer(AgilePlannerDT.Data.StoryCard storycard)
        {
            int index = cards.IndexOf(storycard);
            if (index >= 0)
            {
                cards.Insert(index, storycard);
            }
            else
            {
                cards.Add(storycard);
            }
        }

        public AgilePlannerDT.Data.StoryCard Take()
        {
            while (cards.Count <= 0) ;
            StoryCard ret;
            ret = cards[0];
            cards.RemoveAt(0);
            return ret;
        }
    }

}
